/**
 * Super class
 */

function Shape(color, thickness)
{
	/**
	*
	*/
	this.shapeId = 0;
	
	/**
	 * An array off all x points of the Shape
	 */
    this.x = [];
    
    /**
     * An array off all y points of the Shape
     */
    this.y = [];
	
    
    /**
     * Time at which a point has been drawn in ms
     */
    this.time = [];
	
	
	// Caching the shape (used for optimisation)
	this.canvas = null;
	
    
    /**
     * 
     */
    this.scale_x = 1;
    
    /**
     * 
     */
    this.scale_y = 1;
	
	this.breakPoints = [];
    
    
    /**
     * Color of the Shape
     */
    this.color = color;
	
	if (this.color == null)
		this.color = new Color(0, 0, 0, 1);
    
    /**
     * The thickness of the Shape
     */
    this.thickness = thickness;
	
	this.real_x;
	
    this.real_y;
    
    
    //=======================================================
	// These attributes will not be saved in the file !
	// They can be calculated using the arrays of points
	//=======================================================
	 
    
	
	this.real_width;
	
	this.real_height;
    
	this.nw_point = new Point2D;
	
	this.ne_point = new Point2D;
	
	this.sw_point = new Point2D;
	
	this.se_point = new Point2D;
	
	//===================================================
	
	
	this.dir = null;
	
	this.resizePoint = null;
	
	this.rotatePoint = null;
	
	this.movingPoint = null;
	
	this.getResizePoint = function(){
		return this.resizePoint;
	}
	
	this.getRotatePoint = function(){
		return this.rotatePoint;
	}
	
	this.setResizePoint = function(point){
		this.resizePoint = point;
	}
	
	this.getMovingPoint = function(){
		return this.movingPoint;
	}
	
	this.setMovingPoint = function(point){
		this.movingPoint = point;
	}
	
	this.setRotatePoint = function(point){
		this.rotatePoint = point;
	}
	
	
	// Getters and Setters
	
	this.getColor = function(){
		return this.color;
	}
	
	this.setColor = function(color){
		this.color = color;
	}
	
	this.getThickness = function(){
		return this.thickness;
	}
	
	this.setRealX = function(x){
		this.real_x = x;
		this.computeExtremePoints();
	}
	
	this.setRealY = function(y){
		this.real_y = y;
		this.computeExtremePoints();
	}
	
	this.getXPoints = function(){
		return this.x;
	}
	
	this.getYPoints = function(){
		return this.y;
	}
	
	this.getRealX = function(){
		return this.real_x;
	}
	
	this.getRealY = function(){
		return this.real_y;
	}
	
	this.getRealWidth = function(){
		return this.real_width;
	}
	
	this.getRealHeight = function(){
		return this.real_height;
	}
	
	this.getThickness = function(){
		return this.thickness;
	}
	
	this.getCacheCanvas = function(){
		return this.canvas;
	}
	
	this.setCacheCanvas = function(cacheCanvas){
		this.canvas = cacheCanvas;
	}
    
    
    /**
     * Adds a point
     * @param x
     * @param y
     * @param time
     */

    this.addPoint = function(x, y, time) 
    {
		if (this.x.length > 0){
			var lastXPoint = this.x[this.x.length - 1];
			var lastYPoint = this.y[this.y.length - 1];
			
			var xPointSpace = Math.abs(lastXPoint - x);
			var yPointSpace = Math.abs(lastYPoint - y);
			
			//console.log(xPointSpace + "     " + yPointSpace);
			
			if ((xPointSpace > 2 || yPointSpace > 2)){
				this.x.push(x);
				this.y.push(y);
				this.time.push(time);
			}
		}
		else{
			this.x.push(x);
			this.y.push(y);
			this.time.push(time);
		}
		
		/*this.x.push(x);
		this.y.push(y);
		this.time.push(time);*/
    };
    
    this.flushBuffers = function()
    {
    	this.x = [];
    	
    	this.y = [];
    	
    	this.time = [];
    };
    
    this.initShape = function(x,y)
    {
    	this.addPoint(x, y, 0);
		
    	this.addPoint(x+2, y+2, 0);

    };
    
    this.fillShape = function(x,y,t)
    {
    	this.addPoint(x, y, t);
    };
    
    this.addToCache = function(calcReals)
    {
		// Calculating the points
    	var min_x = getMin(this.x);
    	var min_y = getMin(this.y);
    	
    	var max_x = getMax(this.x);
    	var max_y = getMax(this.y);
    	
    	var width = (max_x - min_x) + (this.thickness * 2);
    	var height = (max_y - min_y) + (this.thickness * 2);
		
		
		// Creating the canvas
		var cacheCanvas = document.createElement("canvas");
		
		var ctx = cacheCanvas.getContext("2d");
		
		
		
		this.setCacheCanvas(new Canvas(cacheCanvas, ctx));	
		
    	
		this.canvas.getCanvas().width = width;
		this.canvas.getCanvas().height = height;
		
    	
		// Initialising properties to shape
    	this.canvas.getContext().strokeStyle = this.color.toString();
		
    	this.canvas.getContext().lineWidth = this.thickness;
		
    	this.canvas.getContext().lineJoin = "round";

    	
    	min_x -= this.thickness;
    	min_y -= this.thickness;
    	
		
		// Fast version
    	/*for (var i = 0 ; i < this.x.length ; i++)
    	{
	    	this.canvas.getContext().beginPath();
	    	this.canvas.getContext().moveTo(this.x[i] - min_x, this.y[i] - min_y);
	    	this.canvas.getContext().lineTo(this.x[i+1] - min_x, this.y[i+1] - min_y);
			
	    	this.canvas.getContext().closePath();
	    	this.canvas.getContext().stroke();
    	}*/
		
		// AA Version (smooth edges) for better machines
		
		/*this.canvas.getContext().beginPath();
		this.canvas.getContext().moveTo(this.x[0] - min_x, this.y[0] - min_y);

		for (var i = 1; i < this.x.length - 2; i++) {
			var c = ((this.x[i] - min_x) + (this.x[i+1]-min_x)) / 2;
			var d = ((this.y[i] - min_y) + (this.y[i+1]-min_y)) / 2;
		 
			this.canvas.getContext().quadraticCurveTo(this.x[i] - min_x, this.y[i] - min_y, c, d);
		}
		 
		// For the last 2 points
		this.canvas.getContext().quadraticCurveTo(
			this.x[i] - min_x,
			this.y[i] - min_y,
			this.x[i+1] - min_x,
			this.y[i+1] - min_y
		);
		this.canvas.getContext().stroke();*/
		
		// Multiple joined curves
		
		var startPoint = 1;
		
		if (this.breakPoints.length > 0){
			
			console.log("-----------------------------------------------------------");
			for (var j = 0 ; j < this.breakPoints.length ; j++){
				this.canvas.getContext().beginPath();
				this.canvas.getContext().moveTo(this.x[startPoint-1] - min_x, this.y[startPoint-1] - min_y);
				
				for (var i = startPoint; i < this.breakPoints[j] - 2; i++) {
					var c = ((this.x[i] - min_x) + (this.x[i+1]-min_x)) / 2;
					var d = ((this.y[i] - min_y) + (this.y[i+1]-min_y)) / 2;
					
				 
					this.canvas.getContext().quadraticCurveTo(this.x[i] - min_x, this.y[i] - min_y, c, d);
				}
			 
				// For the last 2 points
				this.canvas.getContext().quadraticCurveTo(
					this.x[i] - min_x,
					this.y[i] - min_y,
					this.x[i+1] - min_x,
					this.y[i+1] - min_y
				);
				
				this.canvas.getContext().stroke();
				
				// Compute new start value
				
				startPoint = this.breakPoints[j] + 1;
			}
		}
		else{
		
			this.canvas.getContext().beginPath();
			this.canvas.getContext().moveTo(this.x[0] - min_x, this.y[0] - min_y);
			
			for (var i = 1; i < this.x.length - 2; i++) {
				var c = ((this.x[i] - min_x) + (this.x[i+1]-min_x)) / 2;
				var d = ((this.y[i] - min_y) + (this.y[i+1]-min_y)) / 2;
			 
				this.canvas.getContext().quadraticCurveTo(this.x[i] - min_x, this.y[i] - min_y, c, d);
			}
		 
			// For the last 2 points
			this.canvas.getContext().quadraticCurveTo(
				this.x[i] - min_x,
				this.y[i] - min_y,
				this.x[i+1] - min_x,
				this.y[i+1] - min_y
			);
			
			this.canvas.getContext().stroke();
		}
		
		
    	
		if(calcReals){
			this.real_x = min_x;
			this.real_y = min_y;
		}
		
		this.real_width = this.canvas.getCanvas().width;
		this.real_height = this.canvas.getCanvas().height;
		
		
		// Points
		this.computeExtremePoints();
    	
    	
    };
    
	/**
	*
	*/
    this.endShape = function()
    { 	
    	this.addToCache(true);
    	
    };
	
	this.isPointInCanvas = function(x,y){
	
    	
    	if ( ((x >= this.getRealX()) && (x <= this.getRealX()+this.getRealWidth())) && 
			 ((y >= this.getRealY()) && (y <= this.getRealY()+this.getRealHeight())) )
    	{
    		return true;
    	}
    	
    	return false;
	}
	
	this.isMouseOverPath = function(x,y){
		
		var min_x = getMin(this.x);
    	var min_y = getMin(this.y);
		
		
		var mouseX = x;
		var mouseY = y;
		
		var translateX = this.getRealX() - min_x;
		var translateY = this.getRealY() - min_y;
		
		for (var i = 0 ; i < this.getXPoints().length ; i++){
			
			var currentXPointBegin = (this.x[i] - this.thickness) + translateX;
			var currentYPointBegin = (this.y[i] - this.thickness) + translateY;
			
			var currentXPointEnd = (this.x[i] + this.thickness) + translateX;
			var currentYPointEnd = (this.y[i] + this.thickness) + translateY;
			
			if ( (mouseX >= currentXPointBegin && mouseX <= currentXPointEnd) && 
				 (mouseY >= currentYPointBegin && mouseY <= currentYPointEnd)){
				 
				this.setMovingPoint(i);
				return true
				 
			}
		}
		return false;
	}
	
	this.isShapeInSelectedBox = function(x, y, w, h){
		
		var min_x = getMin(this.x);
    	var min_y = getMin(this.y);
		
		
		var translateX = this.getRealX() - min_x;
		var translateY = this.getRealY() - min_y;
		
		for (var i = 0 ; i < this.getXPoints().length ; i++){
		
			var currentXPointBegin = (this.x[i] - this.thickness) + translateX;
			var currentYPointBegin = (this.y[i] - this.thickness) + translateY;
			
			var currentXPointEnd = (this.x[i] + this.thickness) + translateX;
			var currentYPointEnd = (this.y[i] + this.thickness) + translateY;
			
			if ((this.x[i] >= x && this.x[i] <= w) && (this.y[i] >= y && this.y[i] <= h))
				return true;
		}
		
		return false;
	}
	
	this.getResizePoints = function(x,y)
    {
		/*
		
		0----------------------------1
		|							|
		|							|
		|							|
		|							|
		|							|
		|							|
		2----------------------------3
		
		*/
		
		console.log("DO WE EVEN ENTER HERE");
			
		if ( ((x >= (this.nw_point.x - this.thickness)) && (x <= (this.nw_point.x + this.thickness)))
		   &&((((y >= (this.nw_point.y - this.thickness)) && (y <= (this.nw_point.y + this.thickness))))))
		   return 0;
		else if ( ((x >= (this.sw_point.x - this.thickness)) && (x <= (this.sw_point.x + this.thickness)))
		   &&((((y >= (this.sw_point.y - this.thickness)) && (y <= (this.sw_point.y + this.thickness))))))
		   return 2;
		else if ( ((x >= (this.ne_point.x - this.thickness)) && (x <= (this.ne_point.x + this.thickness)))
		   &&((((y >= (this.ne_point.y - this.thickness)) && (y <= (this.ne_point.y + this.thickness))))))
		   return 1;
		else if ( ((x >= (this.se_point.x - this.thickness)) && (x <= (this.se_point.x + this.thickness)))
		   &&((((y >= (this.se_point.y - this.thickness)) && (y <= (this.se_point.y + this.thickness))))))
		   return 3;
    	
    	return null;
    };
	
	this.getRotatePoints = function(x,y){
		return null;
	}
    
	
	/*
	@deprecated
	*/
    this.isMouseHover = function(x,y)
    {
		var once = false;
		var min_x = getMin(this.x);
		//console.log(x + "    " + y);
		var x_test = 0;
		var y_test = 0;
		
    	for (var j = 0 ; j < this.x.length ; j++)
		{
			/*if (j != 0){
			x_test = (x / this.scale_x);
			y_test = y;
			}
			else{
			x_test = x;
			}
			
			
			
			
			
			if (this.scale_x != 1){
				x_test += (this.real_width / 2) + (this.thickness * this.scale_x + (this.scale_x*4)) / 2;
				//if (this.scale_x != 1) {x_test += (this.real_width/this.scale_x) - this.thickness}
				//if (this.scale_y != 1) {y_test += (this.real_height/2) - this.thickness*2}
				if (!once){
					console.log("realx --> " + this.x[0] + "    " + this.y[0]);
					console.log("x TESTING ========> " + x_test);
					console.log("width ========> " + this.real_width);
					once = true;
				}
			
			
			}*/
			x_test = x;
			y_test = y;
			if ( ((x_test >= (this.x[j] - this.thickness)) && (x_test <= (this.x[j] + this.thickness))) && 
					((y_test >= (this.y[j] - this.thickness)) && (y_test <= (this.y[j] + this.thickness))))
			{
				temp = j;
				return true;
			}
		}
    	
    	return false;
    };
	
	
    
    
    this.move = function(nbx,nby)
    {
		
    	this.real_x = this.real_x - nbx;
    	this.real_y = this.real_y - nby;
		
    	
		// Optimise here ??
    	for (var i = 0 ; i < this.x.length ; i++)
    	{    		
    		this.x[i] = this.x[i] - nbx;
    		
    		this.y[i] = this.y[i] - nby;
    	}
		
    	this.computeExtremePoints();
    };
    
    this.resize = function(nbx,nby,dir)
    {
    	console.log("Resizing ----> " + dir);

		this.scale_x = ((this.real_width) + (nbx - (this.real_x+this.real_width))) / (this.real_width);
    	
    	this.scale_y = ((this.real_height) + (nby - (this.real_y+this.real_height))) / (this.real_height);
		
		this.dir = dir;
    	
		this.computeExtremePoints();
		
		
    	drawer.setScale(this.scale_x, this.scale_y, DYNAMIC_CONTEXT);
    };
    
    this.rotate = function(degre)
    {
    };
    
	
	this.computeExtremePoints = function()
	{
		this.nw_point.x = this.real_x;
		this.nw_point.y = this.real_y;
		
		this.ne_point.x = this.real_x + this.real_width;
		this.ne_point.y = this.real_y;
		
		this.sw_point.x = this.real_x;
		this.sw_point.y = this.real_y + this.real_height;
		
		this.se_point.x = this.real_x + this.real_width;
		this.se_point.y = this.real_y + this.real_height;
	};
    
    this.drawBox = function(canvas){
		
		// Doesn't work on firefox
		//canvas.getContext().setLineDash([5]);
		
		canvas.getContext().beginPath();
		canvas.getContext().strokeStyle="rgba(0, 0, 255, 1)";
		canvas.getContext().lineWidth="1"; 
		
		canvas.getContext().rect(this.getRealX(),
								 this.getRealY(),
								 this.getRealWidth(),
								 this.getRealHeight());
		
		canvas.getContext().stroke();
		
		// Draw points here
		canvas.getContext().beginPath();
			canvas.getContext().arc(this.nw_point.x, this.nw_point.y, 2, 0, 2 * Math.PI);
			canvas.getContext().fill();
		canvas.getContext().stroke();
		
		canvas.getContext().beginPath();
			canvas.getContext().arc(this.ne_point.x, this.ne_point.y, 2, 0, 2 * Math.PI);
			canvas.getContext().fill();
		canvas.getContext().stroke();
		
		canvas.getContext().beginPath();
			canvas.getContext().arc(this.sw_point.x, this.sw_point.y, 2, 0, 2 * Math.PI);
			canvas.getContext().fill();
		canvas.getContext().stroke();
		
		canvas.getContext().beginPath();
			canvas.getContext().arc(this.se_point.x, this.se_point.y, 2, 0, 2 * Math.PI);
			canvas.getContext().fill();
		canvas.getContext().stroke();
	}
    
    this.getBinarySize = function()
    {
		
		var ret = 1; // 1 because of shape id
		
		
		ret += this.x.length * 6; // x, y, time
		ret += 1; // Thickness
		ret += 4; // Color
		
		ret += 4; // real_x / real_y
		
		// Transformations
		ret += 8;
		
		// BreakPoints
		
		ret += 2;
		
		for (var i = 0 ; i < this.breakPoints.length ; i++){
			ret += 2
		}
		
		// Optionel
		ret += 2;
		
		return ret;
    };
	
	this.generateHexString = function(data, byteoffset)
	{
		// Setting the shape id (free, circle, line ...)
		data.setUint8(byteoffset, this.shapeId);
		
		// Setting general infos
		data.setUint16(byteoffset, this.x.length); byteoffset += 2; // x and y length
		
		// Setting transformations
		data.setFloat32(byteoffset, this.scale_x); byteoffset += 4;
		data.setFloat32(byteoffset, this.scale_y); byteoffset += 4;
		
		// Setting the number of breakpoints
		data.setUint16(byteoffset, this.breakPoints.length);  byteoffset += 2;
		
		// Setting the breakpoints
		for (var i = 0 ; i < this.breakPoints.length ; i++){
			data.setUint16(byteoffset, this.breakPoints[i]);  byteoffset += 2;
		}
		
		// Setting coords
		data.setUint16(byteoffset, this.real_x); byteoffset +=2;
		data.setUint16(byteoffset, this.real_y); byteoffset +=2;

		
		// Setting styles
		data.setUint8(byteoffset, this.thickness);	byteoffset++;
		
		data.setUint8(byteoffset, this.color.red);	byteoffset++;
		
		data.setUint8(byteoffset, this.color.green); byteoffset++;
		
		data.setUint8(byteoffset, this.color.blue);	byteoffset++;
		
		data.setUint8(byteoffset, this.color.alpha); byteoffset++;
		
		// Setting the data
		
		for (var i = 0 ; i < this.x.length ; i++)
		{
			data.setUint16(byteoffset, this.x[i]); byteoffset+=2;
			data.setUint16(byteoffset, this.y[i]); byteoffset+=2;
			
			// Optimise room here !
			data.setUint16(byteoffset, this.time[i]); byteoffset+=2;
		}
		
		// We need this return in order to continue the binary reading at the good offset !
		return byteoffset;
	};
	
	this.loadFromFile = function(data, byteoffset)
	{
		// Purging the shape
		this.flushBuffers();
		
		// Getting general infos
		var length = data.getUint16(byteoffset); byteoffset += 2; // Length for x,y,time
		
		console.log("taille " + length);
		
		// Setting transformations
		this.scale_x = data.getFloat32(byteoffset); byteoffset += 4;
		this.scale_y = data.getFloat32(byteoffset); byteoffset += 4;
		
		// Getting the number of breakpoints
		var numberOfBreakPoints = data.getUint16(byteoffset);  byteoffset += 2;
		
		// Getting the breakpoints
		for (var i = 0 ; i < numberOfBreakPoints ; i++){

			this.breakPoints.push(data.getUint16(byteoffset)); byteoffset += 2;
		}
		
		
		// Getting coords
		this.real_x = data.getUint16(byteoffset); byteoffset += 2;
		this.real_y = data.getUint16(byteoffset); byteoffset += 2;
		
		// Getting styles
		this.thickness = data.getUint8(byteoffset); byteoffset++;
		
		
		
		this.color.red = data.getUint8(byteoffset); byteoffset++;
		
		this.color.green = data.getUint8(byteoffset); byteoffset++;
		
		this.color.blue = data.getUint8(byteoffset); byteoffset++;
		
		this.color.alpha = data.getUint8(byteoffset); byteoffset++;
		
		//console.log(this.color.toString());

		
		// Getting the data
		for (var i = 0 ; i < length ; i++)
		{
			this.addPoint(data.getUint16(byteoffset), 
						  data.getUint16(byteoffset+2),
						  data.getUint16(byteoffset+4));
						  
			byteoffset += 6;
		}
		
		// Add in cache once it's finished
		this.addToCache(false);
		
		return byteoffset;
	};
	
	
	this.clone = function(data)
	{
		
		var shape = new Shape(this.getColor, this.getThickness());
		
		shape.x = this.x.slice(0);
		shape.y = this.y.slice(0);
		shape.time = this.time.slice(0);
		shape.breakPoints = this.breakPoints.slice(0);
		
		shape.color = this.color;
		shape.thickness = this.thickness;
		
		shape.addToCache(true);
		
		return shape;
	};
}